package com.yb.login.center.provider;

import com.yb.auth.core.bean.SysUser;
import com.yb.auth.core.bean.SysUserPassAuthentication;
import com.yb.core.constant.HttpConstant;
import com.yb.core.constant.RedisConstant;
import com.yb.core.exception.YbBusinessException;
import com.yb.core.util.RedisUtil;
import com.yb.login.center.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Objects;

/**
 * Copyright (C), 2022-2022, 姚兵
 * Author: 32210
 * Date: 2022/11/13 18:35
 * FileName: SysUserAuthProvider
 * Description:
 */

@Component
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SysUserAuthProvider implements AuthenticationProvider {
    @Autowired
    ISysUserService sysUserService;
    @Autowired
    PasswordEncoder passwordEncoder;

    @Autowired
    RedisUtil redisUtil;
    private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();

    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        SysUserPassAuthentication sysUserPassAuthentication = (SysUserPassAuthentication) authentication;
        String username = determineUsername(sysUserPassAuthentication);
        String imgUUID = sysUserPassAuthentication.getImgUUID();
        String verifyCode = sysUserPassAuthentication.getVerifyCode();

        String code = redisUtil.get(String.format(RedisConstant.MOBILE_VERIFY_IMG_CODE, imgUUID));

        if(!StringUtils.hasText(code)){
            throw new YbBusinessException(HttpConstant.Response.VERIFY_CODE_EXPIRED);
        }

        if(!code.equals(verifyCode)){
            throw new YbBusinessException(HttpConstant.Response.VERIFY_CODE_ERROR);
        }

        SysUser userDetails = (SysUser) sysUserService.loadUserByUsername(username);
        if(Objects.isNull(userDetails)){
            throw new UsernameNotFoundException("用户不存在");
        }

        Object principal = authentication.getPrincipal();
        if(!passwordEncoder.matches((CharSequence) authentication.getCredentials(),userDetails.getPassword())){
            throw new BadCredentialsException(this.messages
                    .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }

        return createSuccessAuthentication(sysUserPassAuthentication,userDetails);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return SysUserPassAuthentication.class.isAssignableFrom(authentication);
    }
    private String determineUsername(Authentication authentication) {
        return (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
    }

    protected Authentication createSuccessAuthentication(Authentication authentication,SysUser user) {
        // Ensure we return the original credentials the user supplied,
        // so subsequent attempts are successful even with encoded passwords.
        // Also ensure we return the original getDetails(), so that future
        // authentication events after cache expiry contain the details
        SysUserPassAuthentication result = SysUserPassAuthentication.authenticated(user, this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
        result.setDetails(authentication.getDetails());
        result.setSysUser(user);
        log.debug("Authenticated user");
        return result;
    }
}
